﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using GiveAQuiz.Common;
using GiveAQuiz.Client.Data.DataServices;
using GiveAQuiz.Common.Mvvm;
using GiveAQuiz.QuizManager.Interfaces;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System;
using System.Linq;
using System.Windows.Browser;
using System.ComponentModel;
using GiveAQuiz.QuizManager.Views;

namespace GiveAQuiz.QuizManager.ViewModel
{
  [Export("QuizTakerViewModel")]
  public class QuizTakerViewModel : ViewModelBase
  {
    Quiz _quiz = null;
    IQuizModel _theModel = null;
    DispatcherTimer _timer = null;
    DateTime _startTime = DateTime.Now;
    bool _notStarted = true;
    TimeSpan _timeLeft = TimeSpan.FromSeconds(0);
    ObservableCollection<IQuestionType> _questions = new ObservableCollection<IQuestionType>();
    int _currentQuestion = 1;
    bool _hasVisitedAllQuestions = false;
    bool _endingQuiz = false;

    ViewCommand _startCommand;
    ViewCommand _cancelCommand;
    ViewCommand _prevQuestionCommand;
    ViewCommand _nextQuestionCommand;
    ViewCommand _finishQuizCommand;

    [ImportingConstructor]
    public QuizTakerViewModel(IQuizModel model)
    {
      // Set the Initial members
      _theModel = model;

      // Create the commmands
      StartCommand = new ViewCommand(() => StartQuiz(), () => Quiz != null);
      CancelCommand = new ViewCommand(() => CancelQuiz());
      NextQuestionCommand = new ViewCommand(() => MoveCurrentQuestion(1),
        () => CurrentQuestionNumber < Questions.Count);
      PrevQuestionCommand = new ViewCommand(() => MoveCurrentQuestion(-1),
        () => CurrentQuestionNumber > 1);
      FinishQuizCommand = new ViewCommand(() => EndQuiz(),
        () => _hasVisitedAllQuestions);

// Only check the tester if not debug build
#if !DEBUG
      // Ensure Test Taker hasn't taken the test
      CheckTestTakerAsync();
#else
      // Load the Data Asynchronously 
      LoadQuizTakerAsync();
#endif
    }

    public IQuestionType CurrentQuestion
    {
      get
      {
        if (Questions.Count < CurrentQuestionNumber) return null;
        return Questions[CurrentQuestionNumber - 1];
      }
    }

    public Quiz Quiz
    {
      get
      {
        return _quiz;
      }
      private set
      {
        if (_quiz != value)
        {
          _quiz = value;
          RaisePropertyChanged("Quiz");
        }

      }
    }

    public ViewCommand StartCommand
    {
      get
      {
        return _startCommand;
      }
      private set
      {
        if (value != _startCommand)
        {
          _startCommand = value;
          RaisePropertyChanged("StartCommand");
        }
      }
    }

    public ViewCommand CancelCommand
    {
      get
      {
        return _cancelCommand;
      }
      private set
      {
        if (value != _cancelCommand)
        {
          _cancelCommand = value;
          RaisePropertyChanged("CancelCommand");
        }
      }
    }

    public ViewCommand FinishQuizCommand
    {
      get
      {
        return _finishQuizCommand;
      }
      private set
      {
        if (value != _finishQuizCommand)
        {
          _finishQuizCommand = value;
          RaisePropertyChanged("FinishQuizCommand");
        }
      }
    }

    public ViewCommand PrevQuestionCommand
    {
      get { return _prevQuestionCommand; }
      private set
      {
        if (value != _prevQuestionCommand)
        {
          _prevQuestionCommand = value;
          RaisePropertyChanged("PrevQuestionCommand");
        }
      }
    }

    public ViewCommand NextQuestionCommand
    {
      get { return _nextQuestionCommand; }
      private set
      {
        if (value != _nextQuestionCommand)
        {
          _nextQuestionCommand = value;
          RaisePropertyChanged("NextQuestionCommand");
        }
      }
    }

    public ObservableCollection<IQuestionType> Questions
    {
      get { return _questions; }
      private set
      {
        if (_questions != value)
        {
          _questions = value;
          RaisePropertyChanged("Questions");
        }

      }
    }

    public int CurrentQuestionNumber
    {
      get
      {
        return _currentQuestion;
      }
      set
      {
        if (value != _currentQuestion)
        {
          _currentQuestion = value;
          RaisePropertyChanged("CurrentQuestionNumber");
          RaisePropertyChanged("CurrentQuestion");
        }
      }
    }

    public TimeSpan TimeLeft
    {
      get { return _timeLeft; }
      private set
      {
        if (_timeLeft != value)
        {
          _timeLeft = value;
          RaisePropertyChanged("TimeLeft");
        }

      }
    }

    public bool NotStarted
    {
      get { return _notStarted; }
      set
      {
        if (value != _notStarted)
        {
          _notStarted = value;
          RaisePropertyChanged("NotStarted");
        }
      }
    }

    public bool HasTimeLimit
    {
      get
      {
        if (Quiz == null || Quiz.QuizDefinition == null) return true;
        return Quiz.QuizDefinition.TimeLimitInMinutes != 0;
      }
    }

    void StartQuiz()
    {
      // Hide the Starting UI
      this.NotStarted = false;

      // If it has a time limit start the clock
      if (HasTimeLimit)
      {
        // Start the clock
        _startTime = DateTime.Now;

        // Create the timer
        _timer = new DispatcherTimer()
        {
          Interval = TimeSpan.FromMilliseconds(250)
        };
        _timer.Tick += (s, e) =>
          {
            TimeLeft = TimeSpan.FromMinutes(Quiz.QuizDefinition.TimeLimitInMinutes) - (DateTime.Now - _startTime);

            if (TimeLeft.TotalSeconds <= 0)
            {
              _timer.Stop();
              EndQuiz(true);
            }
          };
        _timer.Start();
      }

    }

    void CancelQuiz()
    {
      HtmlPage.Window.Navigate(new Uri("/Quiz/Pick", UriKind.Relative));
    }

    void EndQuiz(bool timeLimitReached = false)
    {
      if (_endingQuiz) return;

      AppMessages.IsBusyMessage.Send(true);

      if (!timeLimitReached)
      {
        if (!ValidateAnswers())
        {
          // Show Error
          AppMessages.ShowMessage.Send("Some of your answers were not complete, please review the questions.");
          AppMessages.IsBusyMessage.Send(false);
          return;
        }
      }

      _endingQuiz = true;
      QuizResult result = null;

      try
      {
        // Create new Result
        result = _theModel.CreateQuizResult(Quiz, App.CurrentApp.TakerName, App.CurrentApp.TakerEMail, App.CurrentApp.TakerLastVisit);

        // Save Answers
        foreach (var q in Questions)
        {
          result.QuizAnswers.Add(new QuizAnswer()
            {
              Id = 0,
              AnswerData = q.GetAnswerData(),
              QuizResultKey = result.Id,
              QuestionDefinitionKey = q.Definition.Id,
              Result = q.GetResult(q.GetAnswerData()).ToString(),
              AnswerText = q.GetAnswerText(q.GetAnswerData()),
              CorrectAnswerText = q.GetCorrectAnswerText()
            });
        }
      }
      catch (Exception ex)
      {
        AppMessages.IsBusyMessage.Send(false);
        AppMessages.ShowMessage.Send(string.Concat("Error occurred while saving: ", ex.Message));
        _endingQuiz = false;
        return;
      }

      _theModel.AddQuizAnswersToModel(result.QuizAnswers);

      _theModel.SaveAsync((ex) =>
        {
          try
          {
            if (ex == null)
            {              
              // Calculate textual result for the test taker
              var total = Questions.Count();
              var right = Questions.Where(a => a.GetResult(a.GetAnswerData()) == QuestionResult.Correct).Count();
              var inconclusive = Questions.Where(a => a.GetResult(a.GetAnswerData()) == QuestionResult.Undetermined).Count();
              var percent = total - inconclusive == 0 ? 0 : Convert.ToInt32(( (Convert.ToDouble(right) / (total - inconclusive)) * 100));

              string resultText = string.Format("{0}% ({1}/{2})", percent, right, total - inconclusive);

              // Show the complete dialog and return to Quiz Pick when complete
              // Since this is in the View Model, this is probably a bad idea we
              // should refactor
              // TODO Refactor
              AppMessages.ShowDialog.Send(new QuizCompleteDialog(resultText),
                () => HtmlPage.Window.Navigate(new Uri("/Quiz/Pick", UriKind.Relative)));

            }
            else
            {
              AppMessages.ShowMessage.Send(string.Concat("Error occurred while saving: ", ex.Message));
            }
          }
          finally
          {
            AppMessages.IsBusyMessage.Send(false);
          }

        });
    }

    bool ValidateAnswers()
    {
      if (Quiz != null && CurrentQuestion != null)
      {
        CurrentQuestion.ValidateAnswer();
        // Check to see that the questions do not have any errors.
        return Questions.Cast<INotifyDataErrorInfo>().All(e => !e.HasErrors);
      }

      return false;
    }

    void MoveCurrentQuestion(int direction)
    {
      if (CurrentQuestion.ValidateAnswer())
      {
        CurrentQuestionNumber += direction;
        if (CurrentQuestionNumber >= Questions.Count)
        {
          _hasVisitedAllQuestions = true;
          FinishQuizCommand.RaiseCanExecuteChanged();
        }

        // Alert controls about changing commands.
        PrevQuestionCommand.RaiseCanExecuteChanged();
        NextQuestionCommand.RaiseCanExecuteChanged();
      }
    }

    private void CheckTestTakerAsync()
    {
      AppMessages.IsBusyMessage.Send(true);

      _theModel.CanTakeQuizAsync(App.CurrentApp.QuizID, App.CurrentApp.TakerEMail, (result, exception) =>
      {
        try
        {
          if (exception != null)
          {
            AppMessages.ShowDialog.Send(new TakerErrorDialog(),
              () => HtmlPage.Window.Navigate(new Uri("/Quiz/Pick", UriKind.Relative)));

          }
          else
          {
            if (!result)
            {
              AppMessages.ShowMessageAndRedirect.Send("You have already taken the test.  Returning you to the Quiz Picking Page.", 
                                                      new Uri("/Quiz/Pick", UriKind.Relative));
            }
            else
            {
              // Load the Data Asynchronously 
              LoadQuizTakerAsync();
            }
          }
        }
        finally
        {
          AppMessages.IsBusyMessage.Send(false);
        }
      });
    

    }



    void LoadQuizTakerAsync()
    {
      ErrorMessage = "";

      AppMessages.IsBusyMessage.Send(true);

      _theModel.GetQuizAsync(App.CurrentApp.QuizID, (result, exception) =>
      {
        try
        {
          if (exception != null)
          {
            AppMessages.ShowDialog.Send(new TakerErrorDialog(),
              () => HtmlPage.Window.Navigate(new Uri("/Quiz/Pick", UriKind.Relative)));

          }
          else
          {
            Quiz = result;
            if (Quiz.QuizDefinition != null)
            {
              foreach (var q in Quiz.QuizDefinition.QuestionDefinitions)
              {
                var qType = QuestionHelper.CreateQuestionType(q.QuestionTypeName);
                if (qType != null)
                {
                  qType.Definition = q;
                  qType.QuestionData = q.QuestionData;
                  Questions.Add(qType);
                }
              }
            }

            // Raise change notification on all properties that are based 
            // on the Quiz being valid.
            RaisePropertyChanged("CurrentQuestion");
            RaisePropertyChanged("HasTimeLimit");
            NextQuestionCommand.RaiseCanExecuteChanged();
            PrevQuestionCommand.RaiseCanExecuteChanged();
            StartCommand.RaiseCanExecuteChanged();
          }
        }
        finally
        {
          AppMessages.IsBusyMessage.Send(false);
        }
      });
    }
  }
}
